/* JavaLog - package tracker 
 * 
 * Copyright (C) 2009  Allyson Lima, Diego Pedro, Victor Freire
 * 
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software 
 * Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this 
 * program.  If not, see <http://www.gnu.org/licenses/>.
 */
package br.edu.ufcg.ccc.javalog.controller;

import br.edu.ufcg.ccc.javalog.exceptions.BadAddressException;
import br.edu.ufcg.ccc.javalog.exceptions.FranchiseControllerException;
import br.edu.ufcg.ccc.javalog.exceptions.FranchiseException;
import br.edu.ufcg.ccc.javalog.exceptions.PackageException;
import br.edu.ufcg.ccc.javalog.model.Address;
import br.edu.ufcg.ccc.javalog.model.Franchise;
import br.edu.ufcg.ccc.javalog.model.Package;
import br.edu.ufcg.ccc.javalog.model.PackageExpress;
import br.edu.ufcg.ccc.javalog.model.PackageNormal;
import br.edu.ufcg.ccc.javalog.model.PackageOneDay;
import br.edu.ufcg.ccc.javalog.model.Validator;

/**
 * Controller for Franchise model object.
 * @author Allyson Lima, Diego Pedro, Victor Freire
 * @version 27/11/09
 */
public class FranchiseController {
	private Franchise franchise;
	
	/**
	 * Constructs a controller for a franchise model object. 
	 * @param f franchise model object to manipulate
	 */
	public FranchiseController(Franchise f) {
		Validator.validateObject(f, new IllegalArgumentException("null franchise received"));
		franchise = f;
	}
	
	/**
	 * Modifies a franchise.
	 * @throws FranchiseControllerException Thrown when the operation fails. Always carries a message suitable for display to the end-user.
	 */
	public void modifyFranchise(String name, String street, String number, String zipcode, String district, boolean hasAirport) throws FranchiseControllerException {
		try {
			Address address = franchise.getAddress();
			address.setStreet(street);
			address.setZipcode(zipcode);
			address.setDistrict(district);
			franchise.setName(name);
			franchise.getAddress().setStreet(street);
			franchise.getAddress().setZipcode(zipcode);
			franchise.getAddress().setDistrict(district);
			franchise.setHasAirport(hasAirport);
		} 
		catch(BadAddressException e) {
			throw new FranchiseControllerException(e.getMessage() != null ? e.getMessage() : "Erro nas informações sobre o local.");
		} 
		catch(FranchiseException e) {
			throw new FranchiseControllerException(e.getMessage() != null ? e.getMessage() : "Erro nas informações sobre a franquia.");
		}
		catch(Exception e) {
			handleUnknownError(e);
		}
	}
	
	/**
	 * Adds a new nearest franchise.
	 * @param f the franchise to add
	 * @throws FranchiseControllerException Franchise is already on the list 
	 */
	public void addNearestFranchise(Franchise f) throws FranchiseControllerException {
		try {
			Validator.validateObject(franchise, new IllegalArgumentException("received null franchise"));
			if(!franchise.addNearestFranchise(f)) throw new FranchiseControllerException("Essas franquias já estão conectadas.");
		} 
		catch (FranchiseException e) {
			throw new FranchiseControllerException(e.getMessage() != null ? e.getMessage() : "Erro ao tentar conectar as franquias.");
		}
		catch(FranchiseControllerException e) {
			throw e;
		}
		catch(Exception e) {
			handleUnknownError(e);
		}
	}
	
	/**
	 * Remove a neearest franchise.
	 * @param f the franchise to remove
	 * @throws FranchiseControllerException when the franchise passed is not connected to this one 
	 */
	public void removeNearestFranchise(Franchise f) throws FranchiseControllerException  {
		try {
			Validator.validateObject(franchise, new IllegalArgumentException("received null franchise"));
			if(!franchise.removeNearestFranchise(f)) throw new FranchiseControllerException("Essas franquias já não estavam conectadas.");
		} 
		catch (FranchiseException e) {
			throw new FranchiseControllerException(e.getMessage() != null ? e.getMessage() : "Erro ao quebrar a conexão entre as franquias.");
		}
		catch(FranchiseControllerException e) {
			throw e;
		}
		catch (Exception e) {
			handleUnknownError(e);
		}
	}
	
	/**
	 * Creates a new package.
	 * @param packageType the type of the package
	 * @param destinationFranchise the destination franchise
	 * @param street source franchise street
	 * @param number source franchise number
	 * @param zipcode source franchise zipcode
	 * @param district source franchise district
	 * @param street2 destination franchise street
	 * @param number2 destination franchise number
	 * @param zipcode2 destination franchise zipcode
	 * @param district2 destination franchise district
	 * @throws FranchiseControllerException bad address information or general failure to create the package
	 */
	public void createPackage(String packageType, Franchise destinationFranchise, String street, String number, String zipcode, String district, String street2, String number2, String zipcode2, String district2) throws FranchiseControllerException {
		// the source franchise is: franchise
		try {
			Validator.validateObject(destinationFranchise, new FranchiseException("Franquia de Destino não selecionada."));
			Address sourceAddress = franchise.getAddress();
			sourceAddress.setStreet(street);
			sourceAddress.setNumber(number);
			sourceAddress.setZipcode(zipcode);
			sourceAddress.setDistrict(district);
			
			Address destinationAddress = destinationFranchise.getAddress();
			destinationAddress.setStreet(street2);
			destinationAddress.setNumber(number2);
			destinationAddress.setZipcode(zipcode2);
			destinationAddress.setDistrict(district2);
			
			Package p;
			if(packageType == "Normal") {
				p = new PackageNormal(sourceAddress, destinationAddress, franchise, destinationFranchise);
			}
			else if(packageType == "24h") {
				p = new PackageOneDay(sourceAddress, destinationAddress, franchise, destinationFranchise);
			}
			else { //else if(packageType == "Expressa") {
				p = new PackageExpress(sourceAddress, destinationAddress, franchise, destinationFranchise);
			}
			
			franchise.createPackage(p);
		}
		catch(BadAddressException e) {
			throw new FranchiseControllerException(e.getMessage() != null ? e.getMessage() : "Erro nas informações sobre o local.");
		}
		catch(PackageException e) {
			throw new FranchiseControllerException(e.getMessage() != null ? e.getMessage() : "Erro ao tentar criar o pacote requisitado.");
		}
		catch(Exception e) {
			e.printStackTrace();
			handleUnknownError(e);
		}
	}
	
	/**
	 * Get package by ID.
	 * @param ID the id of the package.
	 * @return the package if found, null otherwise
	 * @throws FranchiseControllerException on fetch error 
	 */
	public Package getPackage(String ID) throws FranchiseControllerException {
		try {
			if(ID == null || ID.trim().equals("")) throw new FranchiseControllerException("ID inválida");
			ID = ID.toLowerCase();
			Validator.validatePackageID(ID, new FranchiseControllerException("ID inválida"));
			Package pkg = Franchise.getPackages().get(ID);
			if(pkg == null) throw new FranchiseControllerException("Não existe um pacote com esse identificador.");
			return pkg;
		}
		catch(FranchiseControllerException e) {
			throw e;
		}
		catch(Exception e) {
			handleUnknownError(e);
		}
		return null;
	}
	
	/**
	 * Handles unpredicted errors, mostly from bad code.
	 * @param e the exception caught
	 * @throws FranchiseControllerException the processed error for the View
	 */
	private void handleUnknownError(Exception e) throws FranchiseControllerException {
		// TODO log unexpected errors
		throw new FranchiseControllerException(e.getMessage() != null ? e.getMessage() : "Não há informações adicionais sobre esse erro.");
	}
}
